/**
 * 参考：https://blog.csdn.net/qq_25257229/article/details/117969685
 * 支持基本数据类型、键值都是基本数据类型的普通对象
 * 支持Symbol作为对象的key、支持Date和RegExp、Map和Set对象类型、Function对象类型
 * 支持对象的原型、不可枚举属性、循环引用
 * @param {*} target
 * @returns
 */
function deepClone(target) {
  // WeakMap作为记录对象Hash表（用于防止循环引用）
  const map = new WeakMap();

  // 判断是否为object类型的辅助函数，减少重复代码
  function isObject(target) {
    return (
      (typeof target === "object" && target) || typeof target === "function"
    );
  }

  function clone(data) {
    // 基础类型直接返回值
    if (!isObject(data)) {
      return data;
    }
    // 日期或者正则对象则直接构造一个新的对象返回
    if ([Date, RegExp].includes(data.constructor)) {
      return new data.constructor(data);
    }
    // 处理函数对象
    if (typeof data === "function") {
      return new Function("return " + data.toString())();
    }
    // 如果该对象已存在，则直接返回该对象
    const exist = map.get(data);
    if (exist) {
      return exist;
    }

    // 处理Map对象
    if (data instanceof Map) {
      const result = new Map();
      map.set(data, result);
      data.forEach((val, key) => {
        if (isObject(val)) {
          result.set(key, clone(val));
        } else {
          result.set(key, val);
        }
      });
      return result;
    }

    // 处理Set对象
    if (data instanceof Set) {
      const result = new Set();
      map.set(data, result);
      data.forEach((val) => {
        if (isObject(val)) {
          result.add(clone(val));
        } else {
          result.add(val);
        }
      });
      return result;
    }
    // 收集键名（考虑了以Symbol作为key以及不可枚举的属性）
    const keys = Reflect.ownKeys(data);
    // 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
    const allDesc = Object.getOwnPropertyDescriptors(data);
    // 结合 Object 的 create 方法创建一个新对象，并继承传入原对象的原型链， 这里得到的result是对data的浅拷贝
    const result = Object.create(Object.getPrototypeOf(data), allDesc);
    map.set(data, result);
    keys.forEach((key) => {
      const val = data[key];
      if (isObject(val)) {
        result[key] = clone(val);
      } else {
        result[key] = val;
      }
    });
    return result;
  }

  return clone(target);
}

//简洁版本
function deepClone(obj,hash = new WeakMap()){
  if(obj instanceof RegExp) return new RegExp(obj);
  if(obj instanceof Date) return new Date(obj);
  if(obj === null || typeof obj !== 'object'){
    return obj;
  }
  if(hash.has(obj)){
    return hash.get(obj);
  }
  let t = new obj.constructor();
  hash.set(obj,t);
  for(let key in obj){
    if(obj.hasOwnProperty(key)){
      t[key] = deepClone(obj[key],hash);
    }
  }
  return t;
}

// 自优化
function deepClone(obj,hash = new WeakMap()){
  // 判断是否为object类型的辅助函数，减少重复代码
  function isObject(target) {
    return (
      (typeof target === "object" && target) || typeof target === "function"
    );
  }
  if(obj instanceof RegExp) return new RegExp(obj);
  if(obj instanceof Date) return new Date(obj);
  if(obj === null || typeof obj !== 'object'){
    return obj;
  }
  if(hash.has(obj)){
    return hash.get(obj);
  }
  let t = new obj.constructor();
  hash.set(obj,t);
  // 处理Map对象
  if (obj instanceof Map) {
    obj.forEach((val, key) => {
      if (isObject) {
        t.set(key, deepClone(val, hash));
      } else {
        t.set(key, val);
      }
    });
    return t;
  }
  // 处理Set对象
  if (obj instanceof Set) {
    obj.forEach((val, key) => {
      if (isObject(val)) {
        t.add(key, deepClone(val, hash));
      } else {
        t.add(key, val);
      }
    });
    return t;
  }
  // 处理对象、数组
  for(let key in obj){
    if(obj.hasOwnProperty(key)){
      t[key] = deepClone(obj[key],hash);
    }
  }
  return t;
}